﻿using Fuse8.ContentManagementFramework.Administration.Models;
using Fuse8.ContentManagementFramework.BizFacade.Services;
using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Domain.Bases;
using Fuse8.ContentManagementFramework.Domain.Special;
using Fuse8.ContentManagementFramework.Web.ApplicationRuntime;
using Fuse8.DomainFramework.BusinessService;
using Fuse8.DomainFramework.Common.Exceptions;
using Fuse8.DomainFramework.Common.Locator;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Telerik.Web.Mvc;

namespace Fuse8.ContentManagementFramework.Administration.Controllers
{
    public class CmsUserController : CmsAdminController
    {
        #region Fields

        private IEntityBusinessService<CmsUserGroup, int> _userGroupService;

        private CmsUser _currentUser;

        private CmsUserService UserService = new CmsUserService();

        #endregion Fields

        #region Properties

        protected virtual IEntityBusinessService<CmsUserGroup, int> UserGroupService
        {
            get
            {
                if (_userGroupService == null)
                {
                    _userGroupService = new BusinessServiceFacade<CmsUserGroup, int>();
                }

                return _userGroupService;
            }
        }

        protected virtual CmsUser CurrentUser
        {
            get
            {
                return _currentUser ?? (_currentUser = DependencyServiceLocator.Current.GetInstance<ICmsContext>().ContextUser);
            }
        }

        #endregion Properties

        #region View methods

        public virtual ActionResult Index(int? pageSize)
        {
            int currentPageSize = pageSize ?? DefaultPageSize;

            var data = UserInformationService.GetAllUserInformationForManagement(1, currentPageSize);

            ViewBag.PageSize = currentPageSize;
            ViewBag.Total = data.FirstOrDefault().TotalCount;

            return View(data.Select(p => p.LoggedInUser));
        }

        [GridAction(EnableCustomBinding = true)]
        public virtual ActionResult _Index(GridCommand command)
        {
            ActionResult result = null;

            IEnumerable<CmsUserInformation> data = null;
            int currentPageSize = command.PageSize > 0 ? command.PageSize : DefaultPageSize;

            try
            {
                string sortField = null;
                string sortDir = null;
                if (command.SortDescriptors.Count > 0)
                {
                    sortField = command.SortDescriptors[0].Member;
                    sortDir = command.SortDescriptors[0].SortDirection.ToString();
                }

                data = UserInformationService.GetAllUserInformationForManagement(command.Page, currentPageSize, sortField, sortDir);

                result = View(
                    new GridModel<CmsUser>
                    {
                        Data = data.Select(p => p.LoggedInUser),
                        Total = data.FirstOrDefault().TotalCount
                    });
            }
            catch (Exception ex)
            {
                ControllerContext.HttpContext.Response.StatusCode = 500;
                result = Content(ex.Message, "text/html", Encoding.UTF8);
            }

            return result;
        }

        private static CmsEntityMetaInformation MappMetaToModel(IEnumerable<CmsEntityMetaModel> metas)
        {
            CmsEntityMetaInformation entityMetaInformation = new CmsUser().EntityMetaInformation;

            if (metas != null)
            {
                entityMetaInformation.Metas = new List<CmsEntityMeta>();
                foreach (var meta in metas)
                {
                    entityMetaInformation.Metas.Add(new CmsEntityMeta()
                    {
                        EntityMetaValue = meta.Value == null ? null : meta.Value.ToString(),
                        EntityMetaFieldLink = new CmsEntityMetaFieldLink()
                        {
                            IsRequired = meta.IsRequired,
                            DisplayLabel = meta.Label,
                            EditorName = meta.EditorName,
                            EntityMetaField = new CmsEntityMetaField()
                            {
                                MetaFieldName = meta.Name,
                                MetaFieldType = meta.Type
                            }
                        }
                    });
                }
            }

            return entityMetaInformation;
        }

        [HttpGet]
        public virtual ActionResult Add()
        {
            var newUser = new CmsUserContainer(new CmsUser())
            {
                IsAutoGeneratePassword = true,
                IsCanSetPassword = true
            };
            SetModelSources(newUser);
            return View(newUser);
        }

        [HttpPost]
        public virtual ActionResult Add(CmsUserContainer model)
        {
            int i = 0;
            if (model.Metas != null && model.Metas.Any())
            {
                foreach (var meta in model.Metas)
                {
                    if (meta.IsRequired && meta.Value == null)
                        ModelState.AddModelError("Metas[" + i + "].Value", GetText("Please enter {0}", "cms_Users", meta.Name.ToLower()));
                    i++;
                }
            }

            model.EntityMetaInformation = MappMetaToModel(model.Metas);

            var userWithSameEmail = UserService.GetByEmail(model.UserEmail);

            if (userWithSameEmail != null)
            {
                ModelState.AddModelError("UserEmail", GetText("User with the same email is already exists.", "cms_Users"));
            }

            if (model.IsAutoGeneratePassword)
            {
                model.NewPassword = null;
                model.ReNewPassword = null;
                model.IsSendEmail = true;
            }
            else
            {
                if (string.IsNullOrEmpty(model.NewPassword))
                {
                    ModelState.AddModelError("NewPassword", GetText("Please enter new password", "cms_Users"));
                }
            }

            model.IsCanSetPassword = IsUserCanSetPassword(model.Id);

            if (ModelState.IsValid)
            {
                using (var transaction = new BusinessServiceTransaction<CmsUserInformation>())
                {
                    var cmsUser = MembershipService.CreateUser(model.LoginName, model.UserEmail, model.Firstname, false,
                                                               model.SiteCategory, model.Lastname, model.NewPassword, model.IsSendEmail, model.Locale);

                    if (model.SelectedUserGroups != null)
                    {
                        MembershipService.AssignToGroups(cmsUser,
                                                         model.SelectedUserGroups.Select(p => new CmsUserGroup { Id = p }));
                    }

                    if (model.Metas != null)
                    {
                        foreach (var meta in model.Metas)
                        {
                            cmsUser.SetMetaValue(
                                meta.Name,
                                meta.Value,
                                Type.GetType(meta.Type),
                                true);
                        }
                    }

                    transaction.Commit();
                }

                return RedirectToAction("Index");
            }
            SetModelSources(model);
            return View(model);
        }

        private static void SetModelSources(CmsUserContainer model)
        {
            model.AvailableLanguages = new BusinessServiceFacade<CmsSystemLanguage, int>().FindAll();
        }

        [HttpGet]
        public virtual ActionResult Edit(int id)
        {
            if (id > 0)
            {
                var data = UserInformationService.GetUserInformationForManagement(id);
                if ((data != null) && (data.LoggedInUser != null))
                {
                    var model = new CmsUserContainer(data.LoggedInUser) { IsChangePassword = false, IsCanSetPassword = IsUserCanSetPassword(id) };
                    SetModelSources(model);
                    return View(model);
                }
            }

            return View(new CmsUserContainer { IsChangePassword = false });
        }

        [HttpPost]
        public virtual ActionResult Edit(CmsUserContainer model)
        {
            ActionResult result = null;

            model.EntityMetaInformation = MappMetaToModel(model.Metas);
            model.IsCanSetPassword = IsUserCanSetPassword(model.Id);
            if (ModelState.IsValid)
            {
                var cmsUser = new CmsUser
                                  {
                                      Id = model.Id,
                                      LoginName = model.LoginName,
                                      UserEmail = model.UserEmail,
                                      Firstname = model.Firstname,
                                      Lastname = model.Lastname,
                                      IsEnabled = model.IsEnabled,
                                      Locale = model.Locale
                                  };

                bool needToChangePassword = !string.IsNullOrEmpty(model.NewPassword);

                using (var transaction = new BusinessServiceTransaction<CmsUserInformation>())
                {
                    try
                    {
                        if (needToChangePassword)
                        {
                            MembershipService.UpdateUser(cmsUser, model.NewPassword, model.IsSendEmail);
                        }
                        else
                        {
                            MembershipService.UpdateUser(cmsUser, model.IsSendEmail);
                        }

                        if (model.SelectedUserGroups != null)
                        {
                            MembershipService.AssignToGroups(cmsUser,
                                                             model.SelectedUserGroups.Select(
                                                                 p => new CmsUserGroup { Id = p }));
                        }
                        if (model.AvailableUserGroups != null)
                        {
                            MembershipService.RemoveFromGroups(cmsUser,
                                                               model.AvailableUserGroups.Select(
                                                                   p => new CmsUserGroup { Id = p }));
                        }

                        if (model.Metas != null)
                        {
                            foreach (var meta in model.Metas)
                            {
                                cmsUser.SetMetaValue(
                                    meta.Name,
                                    meta.Value,
                                    Type.GetType(meta.Type),
                                    true);
                            }
                        }

                        transaction.Commit();
                        result = RedirectToAction("Index");
                    }
                    catch (ValidationException ex)
                    {
                        ModelState.AddModelError("", ex.Message);
                    }
                }
            }

            ViewBag.UserId = model.Id;
            SetModelSources(model);
            return result ?? View(model);
        }

        public virtual ActionResult UserDetails(int id)
        {
            ActionResult result = null;

            if (id > 0)
            {
                var data = UserInformationService.GetUserInformationForManagement(id);
                if ((data != null) && (data.LoggedInUser != null))
                {
                    result = PartialView(data.LoggedInUser);
                }
            }

            return result ?? PartialView(new CmsUserContainer());
        }

        public virtual ActionResult AccountDetails(int id)
        {
            var allUserGroups = UserGroupService.FindAll();

            IEnumerable<CmsUserGroup> selectedUserGroups = new List<CmsUserGroup>();

            if (id > 0)
            {
                var data = UserInformationService.GetUserInformationForManagement(id);
                if (data != null)
                {
                    selectedUserGroups = data.LoggedInUserGroups ?? new List<CmsUserGroup>();
                }
            }

            var availableUserGroups = id > 0
                ? allUserGroups.Except(selectedUserGroups, new CmsUserGroup())
                : allUserGroups;

            availableUserGroups = GetPermissionGroups(availableUserGroups);

            return PartialView(
                        new CmsUserGroupContainer
                        {
                            CmsUserId = id,
                            AvailableUserGroups = availableUserGroups,
                            SelectedUserGroups = selectedUserGroups
                        });
        }

        private bool IsUserCanSetPassword(int id)
        {
            if (CurrentUser.Id == id)
            {
                return true;
            }

            var data = UserInformationService.GetUserInformationForManagement(CurrentUser.Id);

            if (data != null && data.LoggedInUserGroups != null && data.LoggedInUserGroups.Any())
            {
                return
                    data.LoggedInUserGroups.Any(
                        x =>
                            x.GroupName == PredefinedUserGroups.Developer ||
                            x.GroupName == PredefinedUserGroups.PowerUser);
            }

            return false;
        }

        private IEnumerable<CmsUserGroup> GetPermissionGroups(IEnumerable<CmsUserGroup> groups)
        {
            var permissionGroups = UserInformationService.GetUserInformationForManagement(CurrentUser.Id).LoggedInUserGroups;

            if (permissionGroups.Any(x => x.GroupName == PredefinedUserGroups.Developer))
            {
                return groups;
            }

            if (permissionGroups.All(x => x.GroupName != PredefinedUserGroups.Developer))
            {
                if (permissionGroups.All(x => x.GroupName != PredefinedUserGroups.Developer) && permissionGroups.All(x => x.GroupName != PredefinedUserGroups.PowerUser))
                {
                    return
                        groups.Where(
                            x =>
                                x.GroupName != PredefinedUserGroups.Developer &&
                                x.GroupName != PredefinedUserGroups.PowerUser &&
                                x.GroupName != PredefinedUserGroups.AdministrationAccess);
                }
                if (permissionGroups.All(x => x.GroupName != PredefinedUserGroups.Developer) && permissionGroups.Any(x => x.GroupName == PredefinedUserGroups.PowerUser))
                {
                    return
                        groups.Where(x => x.GroupName != PredefinedUserGroups.Developer);
                }
            }

            return groups;
        }

        #endregion View methods

        protected override bool _Delete(int id)
        {
            bool result = false;

            if (id > 0)
            {
                var data = UserInformationService.GetUserInformationForManagement(id);
                if ((data != null) && (data.LoggedInUser != null))
                {
                    using (var transaction = new BusinessServiceTransaction<CmsUserInformation>())
                    {
                        MembershipService.DeleteUser(data.LoggedInUser);

                        transaction.Commit();

                        result = true;
                    }
                }
            }

            return result;
        }
    }
}